﻿using System;
using System.Linq;
using System.Reflection.Emit;
using msilgc.cbuilder.CSyntaxTree;
using msilgc.cbuilder.MSILSyntaxTree;
using msilgc.CSyntaxTree;

namespace msilgc.cbuilder.CBuilder {
    internal class LoadArgumentReferenceTranslator : ITranslator {
        private readonly VariableFactory _varFactory;

        public LoadArgumentReferenceTranslator(VariableFactory varFactory)
        {
            _varFactory = varFactory;
        }

        private static int ToInt16(byte[] data) {
            return (data[0] << 0x0) | (data[1] << 0x8);
        }

        private static int ToInt8(byte[] data) {
            return (data[0] << 0x0);
        }

        public ICStatement TranslateAndAdd(CMethodBody methodBody, CBasicBlock currentBlock, BasicBlock block, Instruction instruction) {
            int argIndex;
            if(instruction.Inst == OpCodes.Ldarga)
            {
                argIndex = ToInt16(instruction.Data);
            }
            else if(instruction.Inst == OpCodes.Ldarga_S)
            {
                argIndex = ToInt8(instruction.Data);
            }
            else
            {
                throw new Exception("unexpected instruction: " + instruction);
            }
            IVariable source = methodBody.Arguments.ElementAt(argIndex);
            if(source == null)
            {
                if(argIndex == methodBody.BlockIdxVariableOffset || argIndex == methodBody.ThreadIdxVariableOffset ||
                   argIndex == methodBody.ThreadDimVariableOffset || argIndex == methodBody.BlockDimVariableOffset)
                {
                    NoopStatement noopStm = new NoopStatement();
                    currentBlock.Statements.Add(noopStm);
                    return noopStm;
                }
                throw new Exception("unexpected state");
            }
            IVariable target = _varFactory.CreateNext(source.Type);
            AssignToLocalVariable stm = new AssignToLocalVariable(source, target);
            currentBlock.Statements.Add(stm);
            return stm;
        }
    }
}
